<?php
	include_once 'lib/infra/aula.php';
	include_once 'lib/infra/operacao.php';
	
	class AlocacaoHorarioNaoFixo{
		private $listaCursos;
		private $listaDisciplinas = array();
		private $listaSeries;
		private	$listaSalas;
		private $isOrdenaDisciplina;
		private $arquivoSalas;
		private $arquivoDisciplinas;
	
		function loadListas($ordenado = true){
			$fu = new FileUtils();
	    	// Carrega cursos
	    	$lista = $fu->load($this->arquivoDisciplinas);
	    	$this->listaCursos = $fu->splitDisciplinasCurso($lista);
	    	
	    	// Carrega salas
	    	$lista = $fu->load($this->arquivoSalas);
	    	$this->listaSalas = $fu->splitSalas($lista);
	    	
	    	// Cria a lista de disciplinas
			foreach ($this->listaCursos as $curso) {
				foreach ($curso->getSeries() as $serie) {
					$this->listaDisciplinas = array_merge($this->listaDisciplinas, $serie->getListaDisciplinas());
				}
			}
	    	
	    	if ($ordenado) {
		    	
	    		//se for alocacao por disciplina
	    		if ($this->isOrdenaDisciplina){
	    			// Ordena disciplinas
		    		$this->ordenaDisciplinas();
	    		} else{
	    			// Ordena series
	    			$this->ordenaSerie();
	    		}
	    		
		    	// Ordena salas
		    	$this->ordenaSalas();
	    	}
			
		}
		
		static function cmpQtdAlunos($a, $b) {
			return -strcmp($a->getQtdeAlunos(), $b->getQtdeAlunos());
		}
			
		static function cmpQtdAlunosSerie($a, $b) {
			return -strcmp($a->getQtdMediaAlunos(), $b->getQtdMediaAlunos());
		}
		
		static function cmpCapacidade($a, $b) {
			return -strcmp($a->getCapacidade(), $b->getCapacidade());
		}	
		
		function ordenaDisciplinas() {
			$listaRetorno = array();
			
			foreach ($this->listaCursos as $curso) {
				foreach ($curso->getSeries() as $serie) {
					$listaRetorno = array_merge($listaRetorno, $serie->getListaDisciplinas());
				}
			}
			
			usort($listaRetorno, "AlocacaoHorarioNaoFixo::cmpQtdAlunos");
			$this->listaDisciplinas = $listaRetorno;
		}
		
		function ordenaSerie() {
			$listaRetorno = array();
			
			foreach ($this->listaCursos as $curso) {
				$listaRetorno = array_merge($listaRetorno, $curso->getSeries());
			}
			
			usort($listaRetorno, "AlocacaoHorarioNaoFixo::cmpQtdAlunosSerie");
			
			$disciplinas = array();
			foreach ($listaRetorno as $serie) {
				$disciplinas = array_merge($disciplinas, $serie->getListaDisciplinas());
			}
			
			$this->listaDisciplinas = $disciplinas;
		}
		
		function ordenaSalas() {
			$listaRetorno = $this->listaSalas;
			
			usort($listaRetorno, "AlocacaoHorarioNaoFixo::cmpCapacidade");
			
			$this->listaSalas = $listaRetorno;
		}
		
		
		function montarEspacoEstados() {
			$listaDisciplinasAux = array();
			$arvoreOperacoes = array();
			$iterador = 0;
			$retornaOperacao = null;
			
			
			while ($iterador < sizeof($this->listaDisciplinas)) {
				$disciplina = $this->listaDisciplinas[$iterador];
				$countOp = 0;
				foreach ($this->listaSalas as $sala) {
					
					// Verifica se a compara��o ser� feita por alunos na s�rio ou na disciplina
					if ($this->isOrdenaDisciplina) {
						$qtdAlunos = $disciplina->getQtdeAlunos();
					} else {
						$qtdAlunos = $disciplina->getSerie()->getQtdMediaAlunos();
					}
					// Compara quantidade de alunos pela capacidade
					if ($sala->getCapacidade() >= $qtdAlunos) {
						// Divide carga horaria da disciplina
						$c = $disciplina->getCargaHoraria();
						if ($c > 3){
							// Insere operacao na arvore
							$op = $this->montaMatrizAulas($disciplina, $sala, 2, $retornaOperacao);
							if ($op != null) {
								array_push($arvoreOperacoes, $op);
								$countOp++;
							} else {
								continue;
							}
							$c = $c - 2;
						}
						// Insere operacao na arvore
						$op = $this->montaMatrizAulas($disciplina, $sala, $c, $retornaOperacao);
						if ($op != null) {
							array_push($arvoreOperacoes, $op);
							$countOp++;
							break;
						}
					}
				}
				
				/*$retornaOperacao = null;
				
				if (($countOp == 1) && ($disciplina->getCargaHoraria() > 3)){
					// Retira essa disciplina
					echo "Removeu aula dessa disciplina da arvore";
					$op = array_pop($arvoreOperacoes);
					$this->cleanPos($op);
					$countOp = 0;
				}
				if ($countOp == 0) {
					// Remove disciplina anterior
					echo "Retornou um nivel";
					
					$retornaOperacao = array_pop($arvoreOperacoes);
					if ($this->cleanPos($retornaOperacao) == 1) {
						
						if ($retornaOperacao->getAula()->getDisciplina()->getCargaHoraria() > 3) {
							$retornaOperacao = array_pop($arvoreOperacoes);
							$this->cleanPos($retornaOperacao);
						}
					} else {
						echo "<br/>Imprimindo operação";
						var_dump($retornaOperacao);
					}
					
					$iterador -= 2;
				}*/
				
				$iterador++;
				if ($iterador < 0) {break;}
			}
		}
		
		function montaMatrizAulas(&$disciplina, &$sala, $carga, $operacao = null) {
			$dia = 0;
			
			if ($operacao != null) {
				$dia = $operacao->getAula()->getDia() + 1;
			}
			
			// Percorre os dias da semana
			while ($dia < SalaAula::$MAX_DIAS) {
				$hora = 0;
				//verifica se a mesma disciplina n�o foi alocada no mesmo dia
				if (!$this->verificaDisciplinaDia($disciplina, $dia)){
					// Percorre as horas do dia
					while ($hora < SalaAula::$MAX_HORAS-1) {
						if ($hora+$carga-1 < SalaAula::$MAX_HORAS) {
							// Verifica disponibilidade da sala
							if (($sala->getMatrizAulasPos($dia, $hora) == null) && ($sala->getMatrizAulasPos($dia, $hora+$carga-1) == null)) {
								// Verifica se aula n�o para no hor�rio de almo�o
								if (!(($hora < SalaAula::$ALMOCO) && (($hora+$carga-1) >= SalaAula::$ALMOCO))) {
									//Verifica a disponibilidade da Turma
									if(!$this->verificaDisponibilidadeSerie($hora, $dia, $disciplina, $carga)){
										// Verifica a disponibilidade do professor
										if ($this->verificaDisponibilidadeProfessor($disciplina->getProfessor(), $dia, $hora, $carga)) {
											
											for ($i = 0; $i < $carga; $i++) {
												$aula = new Aula();
												$aula->setHora($hora+$i);
												$aula->setDia($dia);
												$aula->setDisciplina($disciplina);
												$aula->setSala($sala);
												
												// Insere a aula na matriz
												$sala->setMatrizAulasPos($aula, $dia, $hora+$i);
												
												// Insere na disciplina
												$disciplina->setAula($aula);
												
												if ($i == 0) {
													//insere operacao na pilha
													$operacao = new Operacao();
													$operacao->setAula($aula);
													$operacao->setQuantidade((int)$carga);
												}
											}
											
											return $operacao;
										}
									}
								}
							}
						}
						$hora++;	
					}
				}
				$hora = 0;
				$dia++;
			}
			return null;
		}
		
		function cleanPos($operacao) {
			if ($operacao != null){
				$aula = $operacao->getAula();
				$sala = $aula->sala;
				
				// Zera a posição
				$sala->setMatrizAulasPos(null, $aula->getDia(), $aula->getHora());
				
				return 1;
			}
			return 0;
		}
		
		
		function verificaDisciplinaDia($disciplina, $dia){
			foreach ($this->listaSalas as $sala) {
				for($i = 0; $i < SalaAula::$MAX_HORAS; $i++){
					$pos = $sala->getMatrizAulasPos($dia, $i);
					if (($pos != null) && ($pos->getDisciplina()->getNome() == $disciplina->getNome())) {
						return true;
					}
				}
			}
			return false;
		}
		
		function verificaDisponibilidadeSerie($hora, $dia, $disciplina, $c){
			foreach ($this->listaSalas as $sala) {
				$pos = $sala->getMatrizAulasPos($dia, $hora);
				
				if ($pos != null) {
					$disciAula = $pos->getDisciplina();
					
					$pos = $sala->getMatrizAulasPos($dia, $hora+$c-1);
					
					if ($pos != null) { 
						$disciAulaFinal = $pos->getDisciplina();
						
						if (($disciAula->getCurso()->getNome() == $disciplina->getCurso()->getNome()) ||
								($disciAulaFinal->getCurso()->getNome() == $disciplina->getCurso()->getNome())) {
							if (($disciAula->getSerie()->getNumero() == $disciplina->getSerie()->getNumero()) || 
									($disciAulaFinal->getSerie()->getNumero() == $disciplina->getSerie()->getNumero())){
								return true;
							}
						}
					}
				}
			}
			return false;
		}
		
		/**
		 * Verifica se o professor est� ocupado, caso esteja retorna false,
		 * se estiver livre retorna true.
		 * @param unknown_type $professor
		 * @param unknown_type $dia
		 * @param unknown_type $hora
		 * @param unknown_type $carga
		 */
		function verificaDisponibilidadeProfessor($professor, $dia, $hora, $carga) {
			foreach ($this->listaSalas as $salas) {
				$pos = $salas->getMatrizAulasPos($dia, $hora);
				if ($pos != null) {
					$disciAula = $pos->getDisciplina();
					
					$pos = $salas->getMatrizAulasPos($dia, $hora+$carga-1);
					if ($pos != null) { 
						$disciAulaFinal = $pos->getDisciplina();
				
						// Verifica se professor j� est� em aula
						if (($disciAula->getProfessor()->getNome() == $professor->getNome()) || 
								($disciAulaFinal->getProfessor()->getNome() == $professor->getNome())) {
							return false;
						}
					}
				}
			}
			return true;
		}
		

		function getListaCursos(){
			return $this->listaCursos;
		}
		
		function getListaSalas(){
			return $this->listaSalas;
		}
		
		function setArquivoSalas($arquivo){
			$this->arquivoSalas = $arquivo;
		}
		
		function setArquivoDisciplinas($arquivo){
			$this->arquivoDisciplinas = $arquivo;
		}
		
		function setOrdenaPorDisciplina($d){
			$this->isOrdenaDisciplina = $d;
		}
		
	}